
package com.kweative.lingua.server.client;


import com.kweative.lingua.server.structures.AppRequest;
import com.kweative.lingua.server.ProtocolPool;
import com.kweative.lingua.server.structures.FileWrapper;
import com.kweative.lingua.server.exceptions.NotFoundException;
import com.kweative.lingua.server.protocol.Protocol;
import com.kweative.lingua.server.protocol.XmlSimpleProtocol;
import com.kweative.lingua.server.request.RequestHandler;
import com.kweative.lingua.server.response.ResponseHandler;
import com.kweative.network.Request;
import com.kweative.network.Response;
import com.kweative.type.Node;
import com.kweative.type.Node.Type;
import com.kweative.util.Benchmark;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author henrik
 */
abstract public class Client extends Thread {
    private RequestHandler request;
    private ResponseHandler response;
    private InputStream in;
    private OutputStream out;

    public Client(RequestHandler request, ResponseHandler response, InputStream in, OutputStream out) {
        this.request = request;
        this.response = response;
        this.in = in;
        this.out = out;
    }

    public RequestHandler getRequest() {
        return request;
    }

    public ResponseHandler getResponse() {
        return response;
    }

    @Override
    public void run() {
        try {
            read();
        } catch (IOException ex) {
            Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    protected void read() throws IOException {
        //Read full request
        request.read(in);
            
        try {
            Protocol requestProtocol = ProtocolPool.instance().getRequestProtocol(request);

            if (requestProtocol == null) {
                response.setStatus(Response.Status.E_FORMAT);
                response.setBody("Could not find valid protocol handler");
            } else {
                //Check for any arguments that could change response protocol
                Protocol responseProtocol = request.getResponseProtocol();
                if (responseProtocol == null) {
                    //If none found - use request protocols default response
                    responseProtocol = requestProtocol.getDefaultResponse();
                    request.setResponseProtocol(responseProtocol);
                }

                AppRequest[] appRequests;
                try {
                    appRequests = requestProtocol.read(request);

                    boolean first = true;
                    boolean isDocumentation = false;

                    Node result = new Node(Type.ARRAY);
                    for(int i = 0; i < appRequests.length;i++) {
                        appRequests[i].setRequestHandler(request);

                        //EXECUTE THE REQUEST:
                        Node tmp = appRequests[i].execute(response);

                        if (appRequests[i].isDocumentiation()) {
                            result = responseProtocol.normalizeDocumentation(appRequests[i],tmp);
                            isDocumentation = true;
                            break;
                        }

                        if (first) {
                            response.setStatus(Response.Status.OK);
                            if (result.getValue() instanceof FileWrapper) {
                                FileWrapper file = (FileWrapper) result.getValue();
                                response.setBody(file.getContents());
                                response.headers().put("Content-Type",file.getMimeType());
                                if (file.getEncoding() != null)
                                    response.headers().put("Content-Encoding",file.getEncoding());
                                break;
                            } else {
                                tmp = responseProtocol.normalizeResponse(appRequests[i],tmp);
                            }
                            first = false;
                        } else {
                            tmp = responseProtocol.normalizeResponse(appRequests[i],tmp);
                        }
                        if (appRequests.length > 1)
                            result.addChild(tmp);
                        else
                            result = tmp;
                    }
                    if (isDocumentation)
                        responseProtocol.writeDocumentation(response,result);
                    else
                        responseProtocol.write(response,result);
                } catch (NotFoundException ex) {
                    response.setStatus(Response.Status.E_NOTFOUND);
                    responseProtocol.writeException(response, ex);
                } catch(Throwable ex) {
                    response.setStatus(Response.Status.E_INTERNAL);
                    responseProtocol.writeException(response, ex);
                    Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        } catch(Throwable ex) {
            Protocol defProto = ProtocolPool.instance().getDefaultProtocol();
            response.setStatus(Response.Status.E_INTERNAL);
            defProto.writeException(response, ex);
            Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
        }
        //Write full response
        try {
            response.write(out);
        } catch (IOException ex) {
            throw ex;
        } catch(Throwable ex) {
            Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
        }
    }


}
